class myPromise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECT = 'reject'
  constructor(func) {
    this.PromiseState = myPromise.PENDING
    this.PromiseResult = null
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    try {
      // 这里this.resolve，this.reject需要绑定当前实例的this，因为这里是创建实例后运行。
      // 我们在创建myPromise实例时传入一个函数，带有2个参数resolve，reject，分别对应这里的this.resolve，this.reject
      func(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error)
    }
  }

  resolve(result) {
    if (this.PromiseState === myPromise.PENDING) {
      setTimeout(() => {
        this.PromiseState = myPromise.FULFILLED
        this.PromiseResult = result
        this.onFulfilledCallbacks.forEach(callback => {
          callback(result)
        })
      })
    }
  }

  reject(reason) {
    if (this.PromiseState === myPromise.PENDING) {
      setTimeout(() => {
        this.PromiseState = myPromise.REJECT
        this.PromiseResult = reason
        // 如果没有catch方法，且返回了错误，这里直接报错
        // if (this.onRejectedCallbacks.length === 0 && reason instanceof Error) {
        //   throw reason
        // }
        // 如果有catch方法，这里callback(reason)
        this.onRejectedCallbacks.forEach(callback => {
          // console.log('xxx 2', this.onRejectedCallbacks, reason)
          callback(reason)
        })
      })
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => {
      throw reason
    }
    const promise2 = new myPromise((resolve, reject) => {
      if (this.PromiseState === myPromise.PENDING) {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.PromiseResult)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.PromiseResult)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
      if (this.PromiseState === myPromise.FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.PromiseResult)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.PromiseState === myPromise.REJECT) {
        setTimeout(() => {
          try {
            let x = onRejected(this.PromiseResult)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
    })
    return promise2
  }

  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  /**
   * finally
   * @param {function} callback 无论结果是fulfilled或者是rejected，都会执行的回调函数
   * @returns 
   */
  finally(callback) {
    return this.then(callback, callback)
  }

  // myPromise.resolve() 写法2
  static resolve(value) {
    if (value instanceof myPromise) {
      return value
    } else if (value instanceof Object && 'then' in value) {
      return new myPromise((resolve, reject) => {
        value.then(resolve, reject)
      })
    }
  
    return new myPromise((resolve) => {
      resolve(value)
    })
  }

  // myPromise.reject() 方法2
  /**
   * myPromise.reject
   * @param {*} reason 表示Promise被拒绝的原因
   * @returns 
   */
  static reject(reason) {
    return new myPromise((resolve, reject) => {
      reject(reason)
    })
  }

  static all(promises) {
    return new myPromise((resolve, reject) => {
      let promiseList = []
      try {
        for(let pItem of promises) {
          promiseList.push(pItem)
        }

        if (Array.isArray(promiseList)) {
          let result = []
          let count = 0

          if (promiseList.length === 0) {
            return resolve(promiseList)
          }

          promiseList.forEach((item, index) => {
            // 如果是myPromise实例，或者有then方法
            myPromise.resolve(item).then(value => {
              count ++
              result[index] = value
              count === promiseList.length && resolve(result)
            }, reason => {
              reject(reason)
            })
          })
        }
      } catch (err) {
        reject(new TypeError('Argument is not iterable'))
      }
    })
  }

  static allSettled(promises) {
    return new myPromise((resolve, reject) => {
      let promiseList = []
      try {
        for(let pItem of promises) {
          promiseList.push(pItem)
        }
      // } catch (err) {
      //   return reject(new TypeError('Argument is not iterable'))
      // }


        if (Array.isArray(promiseList)) {
          let result = []
          let count = 0

          promiseList.forEach((item, index) => {
            myPromise.resolve(item).then(value => {
              count ++
              result[index] = {
                status: 'fulfilled',
                value
              }
              count === promiseList.length && resolve(result)
            }, reason => {
              count ++
              result[index] = {
                status: 'rejected',
                reason: reason
              }
              count === promiseList.length && resolve(result)
            })
          })
        }
      } catch (err) {
        reject(new TypeError('Argument is not iterable'))
      }
    })
  }

  static any(promises) {
    return new myPromise((resolve, reject) => {
      if (Array.isArray(promises)) {
        let errors = []
        let count = 0

        if (promises.length === 0) {
          return reject(new Error('All Promises were rejected'))
        }

        promises.forEach(item => {
          myPromise.resolve(item).then(value => {
            resolve(value)
          }, reason => {
            count ++
            errors.push(reason)
            count === promises.length && reject(errors)
          })
        })
      } else {
        return reject(new TypeError('Argument is not iterable'))
      }
    })
  }

  static race(promises) {
    return new myPromise((resolve, reject) => {
      if (Array.isArray(promises)) {
        if (promises.length > 0) {
          promises.forEach(item => {
            myPromise.resolve(item).then(resolve, reject)
          })
        }
      } else {
        return reject(new TypeError('Arugment is not iterable'))
      }
    })
  }
}

/**
 * 
 * @param {myPromise} promise2 promise1.then方法返回的新的myPromise对象
 * @param {*} x promise1中onFulfilled或onRejected的返回值
 * @param {function} resolve promise2的resolve方法
 * @param {function} reject promise2的reject方法
 */
function resolvePromise(promise2, x, resolve, reject) {
  if (x === promise2) {
    return reject(new TypeError('Chaining cycle detected for promise'))
  }

  // if (x instanceof myPromise) {
  //   if (x.PromiseState === myPromise.PENDING) {
  //     x.then(y => {
  //       resolvePromise(promise2, y, resolve, reject)
  //     }, reject)
  //   } else if (x.PromiseState === myPromise.FULFILLED) {
  //     resolve(x.PromiseResult)
  //   } else if (x.PromiseState === myPromise.REJECT) {
  //     reject(x.PromiseResult)
  //   }
  // } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      var then = x.then
    } catch (e) {
      return reject(e)
    }

    if (typeof then === 'function') {
      let called = false
      try {
        then.call(x, y => {
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, r => {
          if (called) return
          called = true
          reject(r)
        })
      } catch (e) {
        if (called) return
        called = true
        reject(e)
      }
    } else {
      resolve(x)
    }
  } else {
    return resolve(x)
  }
}

// myPromise.resolve() 方法1
// myPromise.resolve = function(value) {
//   if (value instanceof myPromise) {
//     return value
//   } else if (value instanceof Object && 'then' in value) {
//     return new myPromise((resolve, reject) => {
//       value.then(resolve, reject)
//     })
//   }

//   return new myPromise((resolve) => {
//     resolve(value)
//   })
// }

// myPromise.reject() 方法1
// myPromise.reject = function (reason) {
//   return new Promise((resolve, reject) => {
//     reject(reason)
//   })
// }

// ====================== promises-aplus-tests 测试需要 ========================
myPromise.deferred = function() {
  let result = {}
  result.promise = new myPromise((resolve, reject) => {
    result.resolve = resolve
    result.reject = reject
  })
  return result
}

module.exports = myPromise
// ====================== promises-aplus-tests 测试需要 ========================